﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Security;
using System.Web;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Exchange.WebServices.Data;
using Microsoft.Exchange.WebServices.Autodiscover;
using Microsoft.BusinessData.MetadataModel;
using System.Security.Cryptography;
using Microsoft.BusinessData.Runtime;
using System.IO;

namespace MsExchangeBcsConnector.ExchangeBdcModel.Helpers
{
    public class ExchangeHelper
    {

        private static ExchangeService ExchangeServiceSingleton = null;

#region Property Methods
       

#endregion

        internal static ExchangeService GetExchangeServiceConnection(ILobSystemInstance LobSystemInstance)
        {

            if (ExchangeServiceSingleton == null)
            {
                InitExchangeServiceConnection(LobSystemInstance);
            }

            return ExchangeServiceSingleton;
        }


        private static void InitExchangeServiceConnection(ILobSystemInstance LobSystemInstance)
        {
           
                ServicePointManager.ServerCertificateValidationCallback = delegate(object sender,
                               X509Certificate certificate,
                               X509Chain chain,
                               SslPolicyErrors sslPolicyErrors)
               {
                   return true;
               };


                ExchangeServiceSingleton = new ExchangeService(ExchangeVersion.Exchange2010_SP1);
                string EmailDomainForCrawlingValue = LobSystemInstanceHelper.EmailDomainForCrawling(LobSystemInstance);
                string sExchangePassword = LobSystemInstanceHelper.ExchangePassword(LobSystemInstance);
                string sExchangeLogin = LobSystemInstanceHelper.ExchangeLogin(LobSystemInstance);
                string sExchangeServiceUrl = LobSystemInstanceHelper.ExchangeServiceUrl(LobSystemInstance);
            try
            {
                ExchangeServiceSingleton.Credentials = new WebCredentials(sExchangeLogin, sExchangePassword, EmailDomainForCrawlingValue);
                ExchangeServiceSingleton.Url = new Uri(sExchangeServiceUrl);
                // This is because by default, .NET checks whether SSL certificates are signed by a certificate from the Trusted Root Certificate store. 
                ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
            }
            catch (Exception ex)
            {
                LoggingService.LogCritical("Error ceoonection to Exchange web services {0} on behalf of user {1} from domain {2}",
                    sExchangeServiceUrl, sExchangeLogin, EmailDomainForCrawlingValue);
                LoggingService.LogError(ex);
                throw ex;
            }

        }


        private static Item LoadMessageFromEws(ILobSystemInstance LobSystemInstance, string MessageId)
        {
            ExchangeService service = ExchangeHelper.GetExchangeServiceConnection(LobSystemInstance);
            PropertySet mPropertySet = new PropertySet(BasePropertySet.FirstClassProperties, EmailMessageSchema.From);
            mPropertySet.RequestedBodyType = BodyType.Text;

            //MessageId = HexString2Base64(MessageId);
            Item foundedItem = Item.Bind(service, new ItemId(MessageId), mPropertySet);

            if (foundedItem == null) // Found nothing 
                throw new ApplicationException(String.Format("Can't find EMail with Id = {0}", MessageId));

             foundedItem.Load(mPropertySet);

            return foundedItem;
        }

        public static byte[] LoadMessageMimeContent(ILobSystemInstance LobSystemInstance, string MessageId,
            out string CharacterSet, out string Name)
        {
            try
            {
                ExchangeService service = ExchangeHelper.GetExchangeServiceConnection(LobSystemInstance);
                PropertySet mPropertySet = new PropertySet(ItemSchema.MimeContent, ItemSchema.Subject);
                Item foundedItem = Item.Bind(service, new ItemId(MessageId), mPropertySet);
                if (foundedItem == null)
                    throw new ApplicationException(String.Format("Can't find EMail with Id = {0}", MessageId));


                CharacterSet = foundedItem.MimeContent.CharacterSet;
                Name = foundedItem.Subject;
                return foundedItem.MimeContent.Content;
            }
            catch (Exception ex)
            {
                LoggingService.LogCritical("Error loading Mime content for message id {0}", MessageId);
                LoggingService.LogError(ex);
                throw ex;
            }
        }

        public static Message LoadMessage(ILobSystemInstance LobSystemInstance, string MessageId, string MailBoxAddress, string BcsProfile_HostURL)
        {

            try
            {
                Item foundedItem = LoadMessageFromEws(LobSystemInstance, MessageId);
                return new Message()
                {
                    MessageID = foundedItem.Id.UniqueId,
                    EMail = MailBoxAddress,
                    Subject = string.IsNullOrEmpty(foundedItem.Subject) ? string.Empty : foundedItem.Subject.ToString(),
                    Body = (foundedItem.Body == null) ? string.Empty : foundedItem.Body.Text,
                    To = (foundedItem.DisplayTo == null) ? string.Empty : foundedItem.DisplayTo.ToString(),
                    From = (foundedItem[EmailMessageSchema.From] == null) ? string.Empty : foundedItem[EmailMessageSchema.From].ToString(),
                    Date = foundedItem.DateTimeReceived,
                    Url = Message.FormatDisplayUrl(BcsProfile_HostURL, LobSystemInstance.Name, foundedItem.Id.UniqueId),
                   // SecurityDesciptor = MailBoxSecurityCache.GetMailboxSecurity(MailBoxAddress),
                };
            }
            catch (Exception ex)
            {
                LoggingService.LogCritical("Error loading message Id={0} from mailbox={1}", MessageId, MailBoxAddress);
                LoggingService.LogError(ex);
                throw ex;
            }
        }


        public static IEnumerable<Attachment> ReadMessageAttachmentsList(ILobSystemInstance LobSystemInstance, string BcsProfile_HostURL, 
            string MessageId, string MailBoxAddress)
        {
            ExchangeService service = ExchangeHelper.GetExchangeServiceConnection(LobSystemInstance);
            //MessageId = HexString2Base64(MessageId);
            try
            {
                EmailMessage message = EmailMessage.Bind(service, new ItemId(MessageId),
                    new PropertySet(BasePropertySet.IdOnly, ItemSchema.Attachments));

                var selectedAttachments = from attach in message.Attachments
                                          where attach is FileAttachment
                                          select new Attachment()
                                          {
                                              AttachmentID = attach.Id, //Base64String2Hex(attach.Id),
                                              MessageID = MessageId,//Base64String2Hex(MessageId),
                                              EMail = MailBoxAddress,
                                              Name = attach.Name,
                                              Size = attach.Size,
                                              ContentType = attach.ContentType,
                                              LastModified = attach.LastModifiedTime,
                                              Url = Attachment.FormatDisplayUrl(BcsProfile_HostURL, LobSystemInstance.Name, MessageId, attach.Id),
                                              //SecurityDesciptor = MailBoxSecurityCache.GetMailboxSecurity(MailBoxAddress),
                                          };


                return selectedAttachments.ToList<Attachment>();
            }
            catch (Microsoft.Exchange.WebServices.Data.ServiceLocalException)
            {
                return null; // This is an Appointment and doesn't have attachment
            }
            catch (Exception ex)
            {
                LoggingService.LogCritical("Error loading attachments for message id {0} from mailbox {1}", MessageId, MailBoxAddress);
                LoggingService.LogError(ex);
                throw ex;
            }
        }


        public static Attachment LoadAttachement(ILobSystemInstance LobSystemInstance,
            string AttachementId, string MessageId, string MailBoxAddress, bool loadStream, string BcsProfile_HostURL)
        {
            ExchangeService service = ExchangeHelper.GetExchangeServiceConnection(LobSystemInstance);
           // MessageId = HexString2Base64(MessageId);
           // AttachementId = HexString2Base64(AttachementId);

            try
            {
                EmailMessage message = EmailMessage.Bind(service, new ItemId(MessageId),
                    new PropertySet(BasePropertySet.IdOnly, ItemSchema.Attachments));
                message.Load();

                if (message == null || message.Attachments == null)
                        return null;

                    // In this example, theStream is a Stream object into which the attachment contents is loaded.
                    FileAttachment fileAttachment = message.Attachments
                        .FirstOrDefault(a => a is FileAttachment && a.Id == AttachementId) as FileAttachment;

                    if (fileAttachment == null)
                        return null;

                    Attachment returnValue = new Attachment()
                               {
                                   EMail = MailBoxAddress,
                                   MessageID = MessageId,//Base64String2Hex(MessageId),   
                                   AttachmentID = fileAttachment.Id, //Base64String2Hex(fileAttachment.Id),
                                   Name = fileAttachment.Name,
                                   ContentType = fileAttachment.ContentType,
                                   LastModified = fileAttachment.LastModifiedTime,
                                   Url = Attachment.FormatDisplayUrl(BcsProfile_HostURL, LobSystemInstance.Name, MessageId, AttachementId),
                                   // SecurityDesciptor = MailBoxSecurityCache.GetMailboxSecurity(MailBoxAddress),
                               };
                    if (loadStream)
                    {
                        fileAttachment.Load();
                        MemoryStream attachmentStream = new MemoryStream(fileAttachment.Content);

                        returnValue.AttachmentStream = attachmentStream;
                    }

                    return returnValue;
                
            }catch(Microsoft.Exchange.WebServices.Data.ServiceLocalException){
 
                return null;
            }
            catch (Exception ex)
            {
                LoggingService.LogCritical("Error loading attachments list of folders from mailbox {0}", MailBoxAddress);
                LoggingService.LogError(ex);
                throw ex;
            }

        }


        public static IEnumerable<MailFolder> ReadMailboxFoldersList(ILobSystemInstance LobSystemInstance, string MailBoxAddress)
        {
            ExchangeService service = ExchangeHelper.GetExchangeServiceConnection(LobSystemInstance);
            try
            {
                Mailbox mb = new Mailbox(MailBoxAddress);
                FolderId rootFolderId = new FolderId(WellKnownFolderName.MsgFolderRoot, mb);

                // Define the meeting location property. 
                ExtendedPropertyDefinition folderModifiedTime = new ExtendedPropertyDefinition(0x6709, MapiPropertyType.SystemTime);
                ExtendedPropertyDefinition deletedCount = new ExtendedPropertyDefinition(0x670B, MapiPropertyType.Integer);

                FindFoldersResults rlFolders = null;
                FolderView fvFolderView = new FolderView(Constants.MaxItemsCount);
                fvFolderView.PropertySet = new PropertySet(FolderSchema.DisplayName, FolderSchema.Id, folderModifiedTime, deletedCount);

                fvFolderView.Traversal = FolderTraversal.Deep;
                ExtendedPropertyDefinition PR_FOLDER_TYPE = new ExtendedPropertyDefinition(13825, MapiPropertyType.Integer);
                SearchFilter sfSearchFilter = new SearchFilter.IsNotEqualTo(PR_FOLDER_TYPE, "2");
                
                // rlFolders = service.FindFolders(rootFolderId, sfSearchFilter, fvFolderView);
                // FindFoldersResults ffResults;
                List<MailFolder> folderList = new List<MailFolder>();
                do
                {
                    rlFolders = service.FindFolders(rootFolderId, sfSearchFilter, fvFolderView);
                    if (rlFolders != null && rlFolders.Count<Folder>() > 0)
                    {

                        var selectedFolders = from fld in rlFolders
                                              select new MailFolder()
                                              {
                                                  MailFolderID = fld.Id.UniqueId,
                                                  EMail = MailBoxAddress,
                                                  Name = fld.DisplayName,
                                                  LastModifiedTime = GetPropertyVal<DateTime>(fld.ExtendedProperties, folderModifiedTime),
                                                  DeletedCount = GetPropertyVal<int>(fld.ExtendedProperties, deletedCount),
                                                 // SecurityDesciptor = MailBoxSecurityCache.GetMailboxSecurity(MailBoxAddress),
                                              };
                        folderList.AddRange(selectedFolders);
                    }

                    fvFolderView.Offset += rlFolders.Folders.Count;
                } while (rlFolders.MoreAvailable == true);

                return folderList;
            }
            catch (Exception ex)
            {
                LoggingService.LogCritical("Error loading attachments list of folders from mailbox {0}", MailBoxAddress);
                LoggingService.LogError(ex);
                throw ex;
            }
        }

        private static T GetPropertyVal<T>(ExtendedPropertyCollection vals, ExtendedPropertyDefinition defs)
        {           
            try
            {
                 return (T)vals.FirstOrDefault<ExtendedProperty>(p => p.PropertyDefinition.Tag == defs.Tag).Value;                
            }
            catch(Exception ex)
            {
                LoggingService.LogCritical("Error loading value of Exchange item property {0}", defs.Name);
                LoggingService.LogError(ex);
                return default(T);
            }
            
        }
       

        public static MailFolder ReadSpecificMailFolder(ILobSystemInstance LobSystemInstance,
            string MailBoxAddress, string MailFolderId)
        {
                ExchangeService service = ExchangeHelper.GetExchangeServiceConnection(LobSystemInstance);
                try
                {
                    // Define the meeting location property. 
                    ExtendedPropertyDefinition folderModifiedTime = new ExtendedPropertyDefinition(0x6709, MapiPropertyType.SystemTime);
                    ExtendedPropertyDefinition deletedCount = new ExtendedPropertyDefinition(0x670B, MapiPropertyType.Integer);
                    PropertySet mPropertySet = new PropertySet(BasePropertySet.FirstClassProperties, folderModifiedTime, deletedCount);

                    Folder foundedItem = Folder.Bind(service, new FolderId(MailFolderId), mPropertySet);

                    if (foundedItem == null) // Found nothing 
                        return null;

                    return new MailFolder()
                    {
                        MailFolderID = foundedItem.Id.UniqueId,
                        EMail = MailBoxAddress,
                        Name = foundedItem.DisplayName,
                       // SecurityDesciptor = MailBoxSecurityCache.GetMailboxSecurity(MailBoxAddress),
                    };
                }
                catch (Exception ex)
                {
                    LoggingService.LogCritical("Error loading folder id {0} from mailbox {1}", MailFolderId, MailBoxAddress);
                    LoggingService.LogError(ex);
                    throw ex;
                }
        }


        public static void ReadFolderMessagesList(ILobSystemInstance LobSystemInstance, string BcsProfile_HostURL, ref MailBatch  Batch)
        {

            ExchangeService service = ExchangeHelper.GetExchangeServiceConnection(LobSystemInstance);

            FolderId cid1 = new FolderId(Batch.MailFolderId);

            ItemView exmsgView = new ItemView(Constants.MaxBatchSize);
            // The search filter to get emails.
            SearchFilter sf = new SearchFilter.Exists(EmailMessageSchema.From);
            
            exmsgView.Offset = Batch.Offset;


            try
            {
                exmsgView.PropertySet = new PropertySet(BasePropertySet.IdOnly); ;
                FindItemsResults<Item> messages = service.FindItems(cid1, sf, exmsgView);


                string Address = Batch.MailBoxAddress;
                if (messages != null && messages.Count<Item>() > 0)
                {
                    PropertySet mPropertySet = new PropertySet(BasePropertySet.IdOnly, ItemSchema.Subject,
                        ItemSchema.Body, EmailMessageSchema.From, EmailMessageSchema.DisplayTo, ItemSchema.LastModifiedTime, ItemSchema.Attachments);
                    mPropertySet.RequestedBodyType = BodyType.Text;

                    service.LoadPropertiesForItems(messages, mPropertySet);

                    var selectedMessages = from msg in messages
                                           select new Message()
                                           {
                                               MessageID = msg.Id.UniqueId, //Base64String2Hex(msg.Id.UniqueId),                                        
                                               EMail = Address,
                                               Subject = string.IsNullOrEmpty(msg.Subject) ? string.Empty : msg.Subject.ToString(),
                                               Body = (msg.Body == null) ? string.Empty : msg.Body.Text,
                                               To = (msg.DisplayTo == null) ? string.Empty : msg.DisplayTo.ToString(),
                                               From = (msg[EmailMessageSchema.From] == null) ? string.Empty : msg[EmailMessageSchema.From].ToString(),
                                               Date = msg.LastModifiedTime,
                                               Url = Message.FormatDisplayUrl(BcsProfile_HostURL, LobSystemInstance.Name, msg.Id.UniqueId),
                                              // SecurityDesciptor = MailBoxSecurityCache.GetMailboxSecurity(Address),
                                           };
                    Batch.Mails = selectedMessages.ToArray<Message>();
                }

                if (messages.NextPageOffset.HasValue)
                {
                    Batch.Offset += (int)messages.NextPageOffset;
                    Batch.MoreAvailable = messages.MoreAvailable;
                }
                else
                {
                    Batch.Offset = 0;
                    Batch.MoreAvailable = false;
                }
            }
            catch (Exception ex)
            {
                LoggingService.LogCritical("Error loading batch of emails from folder id {0} of mailbox {1} started from offset {2} ", 
                    Batch.MailFolderId, Batch.MailBoxAddress, Batch.Offset);
                LoggingService.LogError(ex);
                throw ex;
            }
        }

        private static Attachment[] GetMessageAttachments(ILobSystemInstance LobSystemInstance, string BcsProfile_HostURL,
            AttachmentCollection attachmentCollection, string MessageId, string MailBoxAddress)
        {
                var selectedAttachments = from attach in attachmentCollection
                                          where attach is FileAttachment
                                          select new Attachment()
                                          {
                                              AttachmentID = attach.Id, //Base64String2Hex(attach.Id),
                                              MessageID = MessageId,//Base64String2Hex(MessageId),
                                              EMail = MailBoxAddress,
                                              Name = attach.Name,
                                              Size = attach.Size,
                                              ContentType = attach.ContentType,
                                              LastModified = attach.LastModifiedTime,
                                              Url = Attachment.FormatDisplayUrl(BcsProfile_HostURL, LobSystemInstance.Name, MessageId, attach.Id),
                                             // SecurityDesciptor = MailBoxSecurityCache.GetMailboxSecurity(MailBoxAddress),
                                          };

                return selectedAttachments.ToArray<Attachment>();           
        }
    }
}
